package com.jevons.geo_lib.geojson.adapter;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.mapbox.geojson.BoundingBox;
import com.mapbox.geojson.Geometry;
import com.mapbox.geojson.GeometryCollection;

import java.io.IOException;
import java.util.List;

/**
 * Copyright 2020 JxGIS
 *
 * @file GeometryCollectionTypeAdapter
 * @auth linaisheng
 * Created on 2022/7/13.
 * Description：
 */
public final class GeometryCollectionTypeAdapter extends TypeAdapter<GeometryCollection> {
    private volatile TypeAdapter<String> stringTypeAdapter;
    private volatile TypeAdapter<BoundingBox> boundingBoxTypeAdapter;
    private volatile TypeAdapter<List<Geometry>> listGeometryAdapter;
    private final Gson gson;

    public GeometryCollectionTypeAdapter(Gson gson) {
        this.gson = gson;
    }

    @Override
    public void write(JsonWriter jsonWriter, GeometryCollection object) throws IOException {
        if (object == null) {
            jsonWriter.nullValue();
            return;
        }
        jsonWriter.beginObject();
        jsonWriter.name("type");
        if (object.type() == null) {
            jsonWriter.nullValue();
        } else {
            TypeAdapter<String> stringTypeAdapter = this.stringTypeAdapter;
            if (stringTypeAdapter == null) {
                stringTypeAdapter = gson.getAdapter(String.class);
                this.stringTypeAdapter = stringTypeAdapter;
            }
            stringTypeAdapter.write(jsonWriter, object.type());
        }
        jsonWriter.name("bbox");
        if (object.bbox() == null) {
            jsonWriter.nullValue();
        } else {
            TypeAdapter<BoundingBox> boundingBoxTypeAdapter = this.boundingBoxTypeAdapter;
            if (boundingBoxTypeAdapter == null) {
                boundingBoxTypeAdapter = gson.getAdapter(BoundingBox.class);
                this.boundingBoxTypeAdapter = boundingBoxTypeAdapter;
            }
            boundingBoxTypeAdapter.write(jsonWriter, object.bbox());
        }
        jsonWriter.name("geometries");
        if (object.geometries() == null) {
            jsonWriter.nullValue();
        } else {
            TypeAdapter<List<Geometry>> listGeometryAdapter = this.listGeometryAdapter;
            if (listGeometryAdapter == null) {
                TypeToken typeToken = TypeToken.getParameterized(List.class, Geometry.class);
                listGeometryAdapter =
                        (TypeAdapter<List<Geometry>>) gson.getAdapter(typeToken);
                this.listGeometryAdapter = listGeometryAdapter;
            }
            listGeometryAdapter.write(jsonWriter, object.geometries());
        }
        jsonWriter.endObject();
    }

    @Override
    public GeometryCollection read(JsonReader jsonReader) throws IOException {
        if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
            return null;
        }
        jsonReader.beginObject();
        String type = null;
        BoundingBox bbox = null;
        List<Geometry> geometries = null;
        while (jsonReader.hasNext()) {
            String name = jsonReader.nextName();
            if (jsonReader.peek() == JsonToken.NULL) {
                jsonReader.nextNull();
                continue;
            }
            switch (name) {
                case "type":
                    TypeAdapter<String> stringTypeAdapter = this.stringTypeAdapter;
                    if (stringTypeAdapter == null) {
                        stringTypeAdapter = gson.getAdapter(String.class);
                        this.stringTypeAdapter = stringTypeAdapter;
                    }
                    type = stringTypeAdapter.read(jsonReader);
                    break;

                case "bbox":
                    TypeAdapter<BoundingBox> boundingBoxTypeAdapter = this.boundingBoxTypeAdapter;
                    if (boundingBoxTypeAdapter == null) {
                        boundingBoxTypeAdapter = gson.getAdapter(BoundingBox.class);
                        this.boundingBoxTypeAdapter = boundingBoxTypeAdapter;
                    }
                    bbox = boundingBoxTypeAdapter.read(jsonReader);
                    break;

                case "geometries":
                    TypeAdapter<List<Geometry>> listGeometryAdapter = this.listGeometryAdapter;
                    if (listGeometryAdapter == null) {
                        TypeToken typeToken = TypeToken.getParameterized(List.class, Geometry.class);
                        listGeometryAdapter =
                                (TypeAdapter<List<Geometry>>) gson.getAdapter(typeToken);
                        this.listGeometryAdapter = listGeometryAdapter;
                    }
                    geometries = listGeometryAdapter.read(jsonReader);
                    break;

                default:
                    jsonReader.skipValue();

            }
        }
        jsonReader.endObject();
        return GeometryCollection.fromGeometries(geometries, bbox);
    }
}